import { Vector2tuple, Vector3 } from 'three-legacy';
import { MathTool, ParameterObjectType, RENDERORDER } from '@amcax/base';
import { ctx2 } from '@app-cad/module';
import {
  calculatePoint,
  getAngle,
  getDotProduct,
  isEquals,
  pointProjectOnLine,
  RETAIN,
} from '@app-cad/common/utils';
import { AnnotationType, DimensionLinear, LocalOriginType } from './types';
import DimBase from './DimBase';

type CurveTypes =
  | 'Point'
  | 'Line'
  | 'Point-Line'
  | 'Point-Point'
  | 'Line-Line'
  | 'Line-Point';
export class DimLinear extends DimBase {
  // 本地坐标
  private p1Local: Vector3; //本地坐标
  private p2Local: Vector3; //本地坐标
  private point1: Vector3; // 本地坐标
  private point2: Vector3; // 本地坐标
  private point3: Vector3; // 本地坐标
  private point4: Vector3; // 本地坐标
  private lineLength: number;
  private dimRelLength: Vector2tuple;
  public curveTypes: CurveTypes; //曲线类型
  private curvePoints: Vector3[][]; //曲线点
  private relPos: Vector2tuple[]; //相对位置
  private localOriginType: LocalOriginType; // 局部坐标轴原点
  private originPoint: Vector3;
  constructor(attrs: DimensionLinear) {
    super();
    this.setUserData({ type: AnnotationType.DistanceAnno });
    this.init(attrs);
  }
  setLocalOrigin(value: LocalOriginType) {
    this.localOriginType = value;
  }
  // 计算向量
  getLineVerVec(relPos: Vector2tuple[]): Vector2tuple[] {
    if (!relPos) {
      return null;
    }
    switch (this.userData.type) {
      case AnnotationType.DimensionAnno:
      case AnnotationType.DistanceAnno: {
        const { vec } = this.getVec(this.p2Local, this.p1Local);
        return [[vec.y, -vec.x], relPos[0]];
      }
      case AnnotationType.DistanceXAnno: //水平
        return [[0, -1], relPos[0]];
      case AnnotationType.DistanceYAnno: //垂直
        return [[1, 0], relPos[0]];
      default:
        return null;
    }
  }
  init(attrs: DimensionLinear) {
    this.updateDisableIfCurvesFixed(attrs.curveIds);
    this.setDimInfo(attrs);
    if (this.curveTypes === 'Point') {
      return;
    }
    const [point3, point4] = this.getCalPoints();
    this.point3 = point3;
    this.point4 = point4;
    // 设置箭头
    this.setArrow([point3, point4]);
    // 设置标签
    this.createUpdateLabel(
      attrs,
      this.mouseXY,
      Math.abs(this.lineLength),
      ParameterObjectType.Length,
    );
    //  设置线段
    this.createUpdateLines();
    this.relPos = this.getPosLv();
    this.setNormalColor();
    if (this.lineLength === 0) {
      this.update({
        labelPos: this.mouseXY,
      });
    }
  }
  createUpdateLines() {
    this.createUpdateLine('line1', this.extendLine([this.point3, this.point1]));
    if (this.lineLength !== 0) {
      this.createUpdateLine(
        'line2',
        this.extendLine([this.point4, this.point2]),
      );
    } else {
      this.removeLine(['line2']);
    }
    const l1 = this.mouseXY.distanceToSquared(this.point3);
    const l2 = this.mouseXY.distanceToSquared(this.point4);
    const l3 = this.point4.distanceToSquared(this.point3);
    let vertices = [this.point3, this.point4, this.point4];
    if (l3 < l1 || l3 < l2) {
      if (l1 > l2) {
        const [p2, p3] = this.extendLine([this.mouseXY, this.point4], 'minus');
        vertices = [this.point3, p2, p3];
      } else {
        const [p2, p3] = this.extendLine([this.mouseXY, this.point3], 'minus');
        vertices = [this.point4, p2, p3];
      }
      this.createUpdateLine('line3', vertices);
      this.removeLine(['line4']);
    } else {
      const [p1, p2] = this.extendLine([this.mouseXY, this.point3], 'minus');
      this.createUpdateLine('line3', [p1, p2, p2]);
      const [p3, p4] = this.extendLine([this.mouseXY, this.point4], 'minus');
      this.createUpdateLine('line4', [p3, p4, p4]);
    }
    if (this.lineLength === 0) {
      if (
        this.curveTypes === 'Point-Line' ||
        this.curveTypes === 'Line-Point' ||
        this.curveTypes === 'Line-Line'
      ) {
        const p1 =
          this.curveTypes === 'Point-Line'
            ? this.curvePoints[1][0]
            : this.curvePoints[0][0];
        const p2 =
          this.curveTypes === 'Point-Line' ? this.p2Local : this.p1Local;
        this.createUpdateLine('line5', [p1, p2]);
      }
    } else {
      this.removeLine(['line5']);
    }
    this.setRenderOrder(RENDERORDER.LINE - 1);
  }
  zoomUpdate() {
    if (this.point3 && this.point1 && this.point2 && this.point4) {
      this.createUpdateLines();
      this.updateArr(this.point3, this.point4);
      ctx2.viewer.requestRender();
    }
  }
  updateArr(point3: Vector3, point4: Vector3) {
    if (this.lineLength === 0) {
      const p1 = this.curvePoints[0][0];
      const p2 =
        this.curvePoints.length === 1
          ? this.curvePoints[0][1]
          : this.curvePoints[1][0];
      const p = pointProjectOnLine(
        this.mouseXY,
        p1,
        new Vector3().subVectors(p2, p1),
      );
      this.point3 = p;
      this.point4 = this.mouseXY;
      this.updateArrow(
        [this.point3],
        [new Vector3().subVectors(this.point4, this.point3)],
        'vector',
      );
    } else {
      this.point3 = point3;
      this.point4 = point4;
      this.updateArrow([this.point3, this.point4]);
    }
  }
  update(attrs: DimensionLinear) {
    this.setDimInfo(attrs);
    if (this.curveTypes === 'Point') {
      return;
    }
    const [point3, point4] = this.getCalPoints();
    this.updateArr(point3, point4);
    this.createUpdateLines();
    this.createUpdateLabel(
      attrs,
      this.mouseXY,
      this.lineLength,
      ParameterObjectType.Length,
    );
  }
  getPosLv(): Vector2tuple[] {
    const po1 = MathTool.fixNumber(this.dimRelLength[0], RETAIN);
    const po2 = MathTool.fixNumber(this.dimRelLength[1], RETAIN);
    this.userData.points = this.relPos = [[po1, po2]];
    return this.relPos;
  }
  getVal() {
    let sign = 1;
    if (AnnotationType.DistanceXAnno === this.userData.type) {
      sign = Math.sign(this.p1Local.x - this.p2Local.x);
    } else if (AnnotationType.DistanceYAnno === this.userData.type) {
      sign = Math.sign(this.p1Local.y - this.p2Local.y);
    }
    return this.dimLabel.getText() * sign;
  }
  getAngle(point1: Vector3, point2: Vector3) {
    return getAngle(point2, point1);
  }
  // 根据相对位置计算点
  getCalPointsByPos(relPos: Vector2tuple[]) {
    const [x, y] = relPos?.[0] || [0, 0];
    const dimRelLength = relPos?.[1] || [0, 0];
    const vec = new Vector3(x, y, 0);
    const vecVer = new Vector3(-y, x, 0);
    const mouseXY = calculatePoint(vec, dimRelLength[1], this.originPoint);
    return calculatePoint(vecVer, dimRelLength[0], mouseXY);
  }
  getCalPoints() {
    switch (this.userData.type) {
      case AnnotationType.DimensionAnno:
      case AnnotationType.DistanceAnno:
        this.setLineLength(this.p1Local.distanceTo(this.p2Local));
        return this.getCalPointsLine();
      case AnnotationType.DistanceYAnno:
        this.setLineLength(this.p1Local.y - this.p2Local.y);
        return this.getPointAndRelPos('x');
      case AnnotationType.DistanceXAnno:
        this.setLineLength(this.p1Local.x - this.p2Local.x);
        return this.getPointAndRelPos('y');
      default:
        return [null, null];
    }
  }
  setLineLength(line: number) {
    this.lineLength = MathTool.fixNumber(line, 6);
    this.userData.value = this.lineLength;
  }
  // 获取长度尺寸约束的点
  getCalPointsLine() {
    const { vec, vecVer } = this.getVec(this.p2Local, this.originPoint);
    const sign = this.getDot(this.p2Local, vecVer);
    const yL = this.getVerticalDis([this.originPoint, vec, vecVer]);
    const point4 = calculatePoint(vecVer, yL * sign, this.p2Local);
    const point3 = calculatePoint(vecVer, yL * sign, this.p1Local);
    const sign2 = this.getDot(this.originPoint, vec);
    const xL = this.getVerticalDis([this.originPoint, vecVer, vec]);
    this.dimRelLength = [xL * sign2, yL * sign];
    return [point3, point4];
  }
  // 获取相对位置和长度 水平竖直点
  getPointAndRelPos(key: 'x' | 'y' = 'x') {
    const point4 = this.p2Local.clone();
    point4[key] = this.mouseXY[key];
    const point3 = this.p1Local.clone();
    point3[key] = this.mouseXY[key];
    // 计算相对位置
    const vec = key === 'x' ? new Vector3(0, 1, 0) : new Vector3(1, 0, 0); // 向量
    const vecVer = new Vector3(vec.y, -vec.x, 0); // 垂直向量
    const yL = this.getVerticalDis([this.originPoint, vec, vecVer]);
    const xL = this.getVerticalDis([this.originPoint, vecVer, vec]);
    const sign = this.getDot(this.originPoint, vecVer);
    const sign2 = this.getDot(this.originPoint, vec);
    this.dimRelLength = [xL * sign2, yL * sign];
    return [point3, point4];
  }
  getDot(startPoint: Vector3, vec: Vector3) {
    return getDotProduct(startPoint, this.mouseXY, vec) >= 0 ? 1 : -1;
  }
  getOrigin() {
    switch (this.localOriginType) {
      case 'end':
        return this.p2Local.clone();
      case 'center':
        return this.p1Local.clone().add(this.p2Local).multiplyScalar(0.5);
      default:
        return this.p1Local.clone();
    }
  }
  setDimInfo(attrs: DimensionLinear) {
    this.setUserData({
      objects: attrs.curveIds,
      type: attrs.dimConstraint,
    });
    if (attrs?.lineVertices) {
      const type: Array<'Point' | 'Line'> = [];
      const cps = attrs.lineVertices.map((item) => {
        if (item.length === 1) {
          type.push('Point');
        } else if (item.length === 2) {
          type.push('Line');
        }
        const ps = item.map((it) => {
          return new Vector3(it[0], it[1], 0);
        });
        return ps;
      });
      this.curvePoints = cps;
      this.curveTypes = type.join('-') as CurveTypes;
      let vec = null;
      let point1, point2;
      switch (this.curveTypes) {
        case 'Line':
          this.p1Local = point1 = cps[0][0];
          this.p2Local = point2 = cps[0][1];
          break;
        case 'Point':
          this.p1Local = point1 = cps[0][0];
          break;
        case 'Point-Point':
          if (isEquals(cps[1][0], cps[0][0])) {
            cps[0][0].x += 1e-10;
          }
          this.p1Local = point1 = cps[0][0];
          this.p2Local = point2 = cps[1][0];
          break;
        case 'Point-Line':
          vec = cps[1][1].clone().sub(cps[1][0]).normalize();
          this.p1Local = cps[0][0];
          point1 = this.p1Local;
          this.p2Local = pointProjectOnLine(cps[0][0], cps[1][0], vec);
          point2 = cps[1][0];

          break;
        case 'Line-Point':
          vec = cps[0][1].clone().sub(cps[0][0]).normalize();
          this.p1Local = pointProjectOnLine(cps[1][0], cps[0][0], vec);
          this.p2Local = cps[1][0];
          point1 = cps[0][0];
          point2 = this.p2Local;
          break;
        case 'Line-Line':
          vec = cps[0][1].clone().sub(cps[0][0]).normalize();
          this.p1Local = pointProjectOnLine(cps[1][0], cps[0][0], vec);
          this.p2Local = cps[1][0];
          if (isEquals(cps[1][0], cps[0][0])) {
            point1 = cps[0][0];
            point2 = cps[1][1];
          } else {
            point1 = cps[0][0];
            point2 = cps[1][0];
          }
          break;
      }
      if (this.curveTypes !== 'Point') {
        this.point1 = point1;
        this.point2 = point2;
      }
      //初始化四个点的坐标、长度、四条线段
    }
    if (this.curveTypes === 'Point') {
      return;
    }
    if (attrs.localOriginType) {
      this.localOriginType = attrs.localOriginType;
    }
    this.originPoint = this.getOrigin();
    if (attrs.labelPos) {
      this.mouseXY = attrs.labelPos;
    } else {
      if (attrs.relPos?.length > 0) {
        this.relPos = attrs.relPos;
      } else {
        const yL = this.defaultDimPos();
        const xL = this.p1Local.distanceTo(this.p2Local) / 2;
        this.relPos = this.relPos || [[xL, yL]];
      }
      const relPos = this.getLineVerVec(this.relPos);
      this.mouseXY = this.getCalPointsByPos(relPos);
    }
  }
}
